package game;
import java.util.ArrayList;
import java.util.List;

import freeCellBackend.Card;
import freeCellBackend.Deck;
import freeCellDataStructures.Cascade;
import freeCellDataStructures.Cell;
import freeCellDataStructures.Foundation;
import freeCellDataStructures.OrderedCascade;


public class Game {
	
	// GAME CONSTANTS
	final int NUMCELLS = 4;
	final int NUMFOUNDATIONS = 4;
	final int NUMCASCADES = 8;

	// CREATE DATA STRUCTURES FOR GUI AND SOLVER
	public Cell[] cells = new Cell[NUMCELLS];
	public Foundation[] foundations = new Foundation[NUMFOUNDATIONS];
	public Cascade[] cascades = new Cascade[NUMCASCADES];
	int totalNumMoves;
	
	// CREATE DATA STRUCTURES FOR SOLVER
	List<Integer> blankCascades = new ArrayList<Integer>();
	List<Integer> blankCells = new ArrayList<Integer>();

	public Game() {
		setup();
	}
	
	// METHODS USED BY GUI AND SOLVER
	// These methods remove and then add if fail.
	// They could get and then remove if succeed.
	public int locationOfCardInCascade(Card c) {
		for(int i=0;i<cascades.length;i++) {
			if(cascades[i].contains(c)) {
				return i;
			}
		}
		return -1;
	}
	public boolean moveCardToCell(int cascade, int cell) {
		// Really ugly line, no interest to break it up.
		Card temp = cascades[cascade].getOrderedCascade(cascades[cascade].size()-1).removeTop();
		if(cascades[cascade].getOrderedCascade(cascades[cascade].size()-1).size() == 0) {
			cascades[cascade].remove(0);
		}
		if(cells[cell].add(temp)) {
			blankCells.remove(new Integer(cell));
			if(cascades[cascade].size() == 0) {
				blankCascades.add(cascade);
			}
			calculateTotalNumMoves();
			return true;
		}
		cascades[cascade].add(new OrderedCascade(temp), false);
		return false;
	}
	public boolean moveCardToFoundation(int cascade, int foundation) {
		// Really ugly line, no interest to break it up.
		Card temp = cascades[cascade].getOrderedCascade(cascades[cascade].size()-1).removeTop();
		if(cascades[cascade].getOrderedCascade(cascades[cascade].size()-1).size() == 0) {
			cascades[cascade].remove(0);
		}
		if(foundations[foundation].add(temp)) {
			if(cascades[cascade].size() == 0) {
				blankCascades.add(cascade);
				calculateTotalNumMoves();
			}
			return true;
		}
		cascades[cascade].add(new OrderedCascade(temp), false);
		return false;
	}
	// THIS METHOD MAY BE USELESS, IT IS HERE FOR POSSIBLE CONVENIENCE.
	public boolean moveOCToCascade(int cascade1, int cascade2) {
		// Really, really ugly line, no interest to break it up.
		OrderedCascade temp = cascades[cascade1].remove(cascades[cascade1].getOrderedCascade(cascades[cascade1].size()-1).size());
		if (cascades[cascade2].add(temp, false)) {
			if (cascades[cascade1].size() == 0) {
				blankCascades.add(cascade1);
				calculateTotalNumMoves();
			}
			if (cascades[cascade2].size() == 1) {
				blankCascades.remove(new Integer(cascade2));
				calculateTotalNumMoves();
			}
			return true;
		}
		cascades[cascade1].add(temp, false);
		return false;
	}
	public boolean moveOCToCascade(int cascade1, int cascade2, int i) {
		OrderedCascade temp = cascades[cascade1].remove(i);
		if (cascades[cascade2].add(temp, false)) {
			if (cascades[cascade1].size() == 0) {
				blankCascades.add(cascade1);
				calculateTotalNumMoves();
			}
			if (cascades[cascade2].size() == 1) {
				blankCascades.remove(new Integer(cascade2));
				calculateTotalNumMoves();
			}
			return true;
		}
		cascades[cascade1].add(temp, false);
		return false;
	}
	public int locationOfCardInCell(Card c) {
		for (int i=0;i<cells.length;i++) {
			if (cells[i].contains(c)) {
				return i;
			}
		}
		return -1;
	}
	public boolean moveCellToCascade(int cell, int cascade) {
		OrderedCascade temp = new OrderedCascade(cells[cell].remove());
		if (cascades[cascade].add(temp, false)) {
			blankCells.add(cell);
			if (cascades[cascade].size() == 1) {
				blankCascades.remove(new Integer(cascade));
			}
			calculateTotalNumMoves();
			return true;
		}
		cells[cell].add(temp.removeTop());
		return false;
	}
	public boolean moveCellToFoundation(int cell, int foundation) {
		Card temp = cells[cell].remove();
		if(foundations[foundation].add(temp)) {
			blankCells.add(cell);
			calculateTotalNumMoves();
			return true;
		}
		cells[cell].add(temp);
		return false;
	}
	public boolean moveCellToCell(int cell1, int cell2) {
		Card temp = cells[cell1].remove();
		if(cells[cell2].add(temp)) {
			blankCells.add(cell1);
			calculateTotalNumMoves();
			return true;
		}
		cells[cell1].add(temp);
		return false;
	}
	public int locationOfCardInFoundation(Card c) {
		for(int i=0;i<foundations.length;i++) {
			if (foundations[i].get() != null && foundations[i].get().equals(c)) {
				return i;
			}
		}
		return -1;
	}
	
	
	public void calculateTotalNumMoves() {
		int i0 = 1;
		for(int i=0;i<=blankCascades.size();i++)
			i0+=i;
		totalNumMoves = ((blankCells.size()+1)*i0);
	}
	public int getTotalNumMoves() {
		return totalNumMoves;
	}
	
	// METHODS USED BY GUI
	
	// METHODS USED BY SOLVER
	
	//HELPER METHODS
	private void setup() {
		//Initialize data structures
		for (int i = 0; i < NUMCASCADES; i++) {
			cascades[i] = new Cascade();
		}
		for (int i = 0; i < NUMCELLS; i++) {
			cells[i] = new Cell();
		}
		for (int i = 0; i < NUMFOUNDATIONS; i++) {
			foundations[i] = new Foundation();
		}
		//Distribute cards from random deck: ADD MICROSOFT PREDECKS LATER
		Deck temp = new Deck();
		for (int i = 0; i<52; i++) {
			cascades[i%NUMCASCADES].add(new OrderedCascade(temp.remove()), true);
		}
		calculateTotalNumMoves();
		blankCells.add(0);blankCells.add(1);blankCells.add(2);blankCells.add(3);
	}
	
	private void moveOC() {
		
	}
	public String toString() {
		String temp = "";
		for(int i=0;i<cascades.length;i++) {
			temp = cascades[i].toString();
		}
		return temp;
	}
}